vt-d: Do FLR of assigned devices with VT-d
authorKeir Fraser <keir.fraser@citrix.com>
Thu, 24 Jan 2008 14:39:38 +0000 (14:39 +0000)
committerKeir Fraser <keir.fraser@citrix.com>
Thu, 24 Jan 2008 14:39:38 +0000 (14:39 +0000)
Currently there is a pdev_flr() function to do FLR before device
assignment in qemu, but most of devices don't have FLR capability.
What's more, should do FLR before assignment and deassignment for
keeping correct device status. If the device doesn't have FLR
capablility, this patch implemented to enter D3hot and return to D0 to
do FLR. And exposed pdev_flr() in VT-d utils, then it can be invoked
by assignment and deassignment functions.

Signed-off-by: Weidong Han <weidong.han@intel.com>
Signed-off-by: Anthony Xu <anthony.xu@intel.com>
tools/ioemu/hw/pass-through.c
xen/arch/x86/hvm/vmx/vtd/extern.h
xen/arch/x86/hvm/vmx/vtd/intel-iommu.c
xen/arch/x86/hvm/vmx/vtd/utils.c

index 5415019ed576c439b2196207a53ec8b182303b4c..d6fd35b9e9ed18047855f9e26ab697d702a5f8d4 100644 (file)
@@ -56,56 +56,6 @@ static int next_bdf(char **str, int *seg, int *bus, int *dev, int *func)
     return 1;
 }
 
-uint8_t find_cap_offset(struct pci_dev *pci_dev, uint8_t cap)
-{
-    int id;
-    int max_cap = 48;
-    int pos = PCI_CAPABILITY_LIST;
-    int status;
-
-    status = pci_read_byte(pci_dev, PCI_STATUS);
-    if ( (status & PCI_STATUS_CAP_LIST) == 0 )
-        return 0;
-
-    while ( max_cap-- )
-    {
-        pos = pci_read_byte(pci_dev, pos);
-        if ( pos < 0x40 )
-            break;
-
-        pos &= ~3;
-        id = pci_read_byte(pci_dev, pos + PCI_CAP_LIST_ID);
-
-        if ( id == 0xff )
-            break;
-        if ( id == cap )
-            return pos;
-
-        pos += PCI_CAP_LIST_NEXT;
-    }
-    return 0;
-}
-
-void pdev_flr(struct pci_dev *pci_dev)
-{
-    int pos;
-    int dev_cap;
-    int dev_status;
-
-    pos = find_cap_offset(pci_dev, PCI_CAP_ID_EXP);
-    if ( pos )
-    {
-        dev_cap = pci_read_long(pci_dev, pos + PCI_EXP_DEVCAP);
-        if ( dev_cap & PCI_EXP_DEVCAP_FLR )
-        {
-            pci_write_word(pci_dev, pos + PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_FLR);
-            do {
-                dev_status = pci_read_long(pci_dev, pos + PCI_EXP_DEVSTA);
-            } while (dev_status & PCI_EXP_DEVSTA_TRPND);
-        }
-    }
-}
-
 /* Being called each time a mmio region has been updated */
 void pt_iomem_map(PCIDevice *d, int i, uint32_t e_phys, uint32_t e_size,
                   int type)
@@ -273,7 +223,7 @@ static int pt_register_regions(struct pt_dev *assigned_device)
     PCIDevice *d = &assigned_device->dev;
 
     /* Register PIO/MMIO BARs */
-    for ( i=0; i < PCI_BAR_ENTRIES; i++ )
+    for ( i = 0; i < PCI_BAR_ENTRIES; i++ )
     {
         if ( pci_dev->base_addr[i] )
         {
@@ -358,9 +308,6 @@ struct pt_dev * register_real_device(PCIBus *e_bus,
 
     assigned_device->pci_dev = pci_dev;
 
-    /* Issue PCIe FLR */
-    pdev_flr(pci_dev);
-
     /* Assign device */
     machine_bdf.reg = 0;
     machine_bdf.bus = r_bus;
index 47c8808840e3a4f8aedc6f16698f4cde13626be7..6143b935b9ff463911253423b5e7ec0026cac83c 100644 (file)
@@ -34,6 +34,7 @@ extern struct ir_ctrl *ir_ctrl;
 void print_iommu_regs(struct acpi_drhd_unit *drhd);
 void print_vtd_entries(struct domain *d, struct iommu *iommu,
                        int bus, int devfn, unsigned long gmfn);
+void pdev_flr(u8 bus, u8 devfn);
 
 int qinval_setup(struct iommu *iommu);
 int queue_invalidate_context(struct iommu *iommu,
index 355224ec5e553d4b407607cd9370861bbd726a21..4b05f1d87cf456fca7f26469c70ed7617bde2d81 100644 (file)
@@ -1481,6 +1481,7 @@ void return_devices_to_dom0(struct domain *d)
         dprintk(XENLOG_INFO VTDPREFIX,
                 "return_devices_to_dom0: bdf = %x:%x:%x\n",
                 pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+        pdev_flr(pdev->bus, pdev->devfn);
         reassign_device_ownership(d, dom0, pdev->bus, pdev->devfn);
     }
 
@@ -1929,6 +1930,7 @@ int assign_device(struct domain *d, u8 bus, u8 devfn)
              "assign_device: bus = %x dev = %x func = %x\n",
              bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
 
+    pdev_flr(bus, devfn);
     reassign_device_ownership(dom0, d, bus, devfn);
 
     /* Setup rmrr identify mapping */
index 6dc4b0fe38a534fb4188187b8d1a3a90bbe89991..d276ba0e44f937b2e0dc11cbe284739fa51d5366 100644 (file)
@@ -22,7 +22,7 @@
 #include <xen/irq.h>
 #include <xen/spinlock.h>
 #include <xen/sched.h>
-#include <asm/delay.h>
+#include <xen/delay.h>
 #include <asm/iommu.h>
 #include <asm/hvm/vmx/intel-iommu.h>
 #include "dmar.h"
@@ -93,6 +93,101 @@ void disable_pmr(struct iommu *iommu)
             "Disabled protected memory registers\n");
 }
 
+static u8 find_cap_offset(u8 bus, u8 dev, u8 func, u8 cap)
+{
+    u8 id;
+    int max_cap = 48;
+    u8 pos = PCI_CAPABILITY_LIST;
+    u16 status;
+
+    status = read_pci_config_16(bus, dev, func, PCI_STATUS);
+    if ( (status & PCI_STATUS_CAP_LIST) == 0 )
+        return 0;
+
+    while ( max_cap-- )
+    {
+        pos = read_pci_config_byte(bus, dev, func, pos);
+        if ( pos < 0x40 )
+            break;
+
+        pos &= ~3;
+        id = read_pci_config_byte(bus, dev, func, pos + PCI_CAP_LIST_ID);
+
+        if ( id == 0xff )
+            break;
+        else if ( id == cap )
+            return pos;
+
+        pos += PCI_CAP_LIST_NEXT;
+    }
+
+    return 0;
+}
+
+#define PCI_D3hot   (3)
+#define PCI_CONFIG_DWORD_SIZE   (64)
+#define PCI_EXP_DEVCAP_FLR      (1 << 28)
+#define PCI_EXP_DEVCTL_FLR      (1 << 15)
+
+void pdev_flr(u8 bus, u8 devfn)
+{
+    u8 pos;
+    u32 dev_cap, dev_status, pm_ctl;
+    int flr = 0;
+    u8 dev = PCI_SLOT(devfn);
+    u8 func = PCI_FUNC(devfn);
+
+    pos = find_cap_offset(bus, dev, func, PCI_CAP_ID_EXP);
+    if ( pos != 0 )
+    {
+        dev_cap = read_pci_config(bus, dev, func, pos + PCI_EXP_DEVCAP);
+        if ( dev_cap & PCI_EXP_DEVCAP_FLR )
+        {
+            write_pci_config(bus, dev, func,
+                             pos + PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_FLR);
+            do {
+                dev_status = read_pci_config(bus, dev, func,
+                                             pos + PCI_EXP_DEVSTA);
+            } while ( dev_status & PCI_EXP_DEVSTA_TRPND );
+
+            flr = 1;
+        }
+    }
+
+    /* If this device doesn't support function level reset,
+     * program device from D0 t0 D3hot, and then return to D0
+     * to implement function level reset
+     */
+    if ( flr == 0 )
+    {
+        pos = find_cap_offset(bus, dev, func, PCI_CAP_ID_PM);
+        if ( pos != 0 )
+        {
+            int i;
+            u32 config[PCI_CONFIG_DWORD_SIZE];
+            for ( i = 0; i < PCI_CONFIG_DWORD_SIZE; i++ )
+                config[i] = read_pci_config(bus, dev, func, i*4);
+
+            /* Enter D3hot without soft reset */
+            pm_ctl = read_pci_config(bus, dev, func, pos + PCI_PM_CTRL);
+            pm_ctl |= PCI_PM_CTRL_NO_SOFT_RESET;
+            pm_ctl &= ~PCI_PM_CTRL_STATE_MASK;
+            pm_ctl |= PCI_D3hot;
+            write_pci_config(bus, dev, func, pos + PCI_PM_CTRL, pm_ctl);
+            mdelay(10);
+
+            /* From D3hot to D0 */
+            write_pci_config(bus, dev, func, pos + PCI_PM_CTRL, 0);
+            mdelay(10);
+
+            /* Write saved configurations to device */
+            for ( i = 0; i < PCI_CONFIG_DWORD_SIZE; i++ )
+                write_pci_config(bus, dev, func, i*4, config[i]);
+
+            flr = 1;
+        }
+    }
+}
 
 void print_iommu_regs(struct acpi_drhd_unit *drhd)
 {